package cc.rengu.igas.bomp.core.packswitch;

import cc.rengu.igas.bomp.common.constant.BompAppParamConstant;
import cc.rengu.igas.bomp.common.constant.BompTreeNodeConstant;
import cc.rengu.igas.bomp.common.enums.RespCodeEnum;
import cc.rengu.igas.bomp.core.model.UserSessionInfo;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.TxnInfo;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.TxnInfoService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.service.realize.impl.TxnInfoServiceImpl;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;
import org.apache.commons.codec.binary.Base64;

import java.io.File;
import java.io.FileInputStream;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 与管理平台通讯层报文解包前后、组前包后实现类
 * Created by 王成 on 2018/7/17.
 */
public class BompServerPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {
    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        String jsonStr = new String(bytes);
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* 转换XSS攻击的字符 */
        jsonStr = jsonStr.replaceAll("<", "＜").replaceAll(">", "＞");
        String httpServerUrl = xmlTreeUtil.getXmlTreeStringValue("_MESSAGEHEAD/@server_url");
        /*健康检查*/
        String healthChekUrl = xmlTreeUtil.getXmlTreeStringValue("_MESSAGEHEAD/@server_url");
        if (!StringUtil.isEmptyOrNull(healthChekUrl) && (healthChekUrl.contains("healthCheck") || healthChekUrl.contains("nginx.html"))) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID, AppParamConstant.DEFAULT_INSTID);
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_SRC_SYS_ID, "NGINX");
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "HEALCHTK");
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, AppParamConstant.DEFAULT_BIZ_TYPE);
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_CHANNEL_ID, "NGINX");
            return "{}".getBytes();
        }
        String method = xmlTreeUtil.getXmlTreeStringValue("_MESSAGEHEAD/http/method");
        rglog.debug("请求方式:<{}>", method);
        if ("GET".equals(method)) {
            rglog.debug("httpServerUrl:<{}>", httpServerUrl);
            /*get请求特殊处理*/
            String[] serverUrls = httpServerUrl.split("\\?");
            if (serverUrls.length < 2) {
                rglog.error("请求非法");
                return null;
            }
            if (httpServerUrl.contains("/alipay/redirect")) {
                //支付宝支付商户获取应用授权回调请求，截取掉/alipay/redirect&字符串再转换成json字符串
                String requestParam = httpServerUrl.substring(17);
                jsonStr = StringUtil.parseHttpGetParamToJsonString(requestParam);
                JSONObject jsonObject = JSON.parseObject(jsonStr);
                String mchntNo = jsonObject.getString("state");
                if (!StringUtil.isEmptyOrNull(mchntNo)) {
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.MCHNT_NO, mchntNo);
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID, mchntNo.substring(0, 3));
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_SRC_SYS_ID, "ALIP");
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM, "M00BM063");
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, AppParamConstant.DEFAULT_BIZ_TYPE);
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_CHANNEL_ID, "*");
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TRACE_NO, RandomUtil.genSessionId());
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_DATE, DateUtil.getCurrentDate());
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_TIME, DateUtil.getCurrentTime());
                }

            } else {
                String serverUrl = serverUrls[1];
                serverUrl = URLDecoder.decode(serverUrl, "UTF-8");
                jsonStr = serverUrl.replaceAll(" ", "").replaceAll("\\n", "");
                bytes = jsonStr.getBytes();
                rglog.debug("入参报文:<{}>", jsonStr);
                xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.IS_GET_REQUEST, "Y");
            }
        }

        String contentType = xmlTreeUtil.getXmlTreeStringValue("_MESSAGEHEAD/http/content-type");
        if (StringUtil.isEmptyOrNull(contentType)) {
            contentType = xmlTreeUtil.getXmlTreeStringValue("_MESSAGEHEAD/http/Content-Type");
        }

        if (!StringUtil.isEmptyOrNull(contentType) && contentType.contains("multipart/form-data;")) {
            /*表单请求特殊处理*/
            rglog.debug("Form表单请求特殊处理.");
            xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.IS_FORM_REQUEST, "Y");
            PooledByteBufAllocator pooledByteBufAllocator = new PooledByteBufAllocator(true);
            ByteBuf byteBuf = new CompositeByteBuf(pooledByteBufAllocator, true, 1024);
            //Unpooled.wrappedBuffer(bytes);
            byteBuf.writeBytes(bytes);
            FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", byteBuf);
            request.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);

            /*创建HTTP对象工厂*/
            HttpDataFactory factory = new DefaultHttpDataFactory(true);
            /*使用HTTP POST解码器*/
            HttpPostMultipartRequestDecoder httpPostRequestDecoder = new HttpPostMultipartRequestDecoder(factory, request);
//            /*获取HTTP请求对象*/
//            HttpContent httpContent = (HttpContent) request;
//            /*加载对象到加码器*/
//            httpPostRequestDecoder.offer(httpContent);

            String radpString = "";
            String imgBase64String = "";
            String imageName = "";
            List<InterfaceHttpData> interfaceHttpDataList = httpPostRequestDecoder.getBodyHttpDatas();
            for (InterfaceHttpData data : interfaceHttpDataList) {
                if (null == data) {
                    continue;
                }
                /*如果数据类型为文件,则保存到fileUploads对象中*/
                if (InterfaceHttpData.HttpDataType.FileUpload.equals(data.getHttpDataType())) {
                    FileUpload fileUpload = (FileUpload) data;
                    File oriFile = fileUpload.getFile();
                    rglog.debug("文件路径<{}>,文件名:<{}>,文件类型<{}>", oriFile.getPath(), fileUpload.getFilename(), fileUpload.getContentType());
                    imageName = fileUpload.getFilename();

                    //IO流操作
                    FileInputStream fileInputStream = null;
                    byte[] fileBytes = null;
                    try {
                        fileInputStream = new FileInputStream(oriFile);
                        fileBytes = new byte[fileInputStream.available()];
                        fileInputStream.read(fileBytes);
                    } catch (Exception e) {
                        rglog.error(e.getMessage());
                    } finally {
                        //关闭流
                        try {
                            if (null != fileInputStream) {
                                fileInputStream.close();
                            }
                        } catch (Exception e) {
                            rglog.error(e.getMessage());
                        }
                    }

                    //预防空指针
                    if (null != fileBytes) {
                        imgBase64String = new String(Base64.encodeBase64(fileBytes));
                    }
                    rglog.debug("源文件Base64:" + imgBase64String);
                    deleteFile(oriFile.getAbsolutePath());
                }
                /*如果数据类型为参数类型,则保存到body对象中*/
                if (InterfaceHttpData.HttpDataType.Attribute.equals(data.getHttpDataType())) {
                    Attribute attribute = (Attribute) data;
                    File jsonFile = attribute.getFile();

                    //IO流操作
                    FileInputStream fileInputStream = null;
                    byte[] fileBytes = null;
                    try {
                        fileInputStream = new FileInputStream(jsonFile);
                        fileBytes = new byte[fileInputStream.available()];
                        fileInputStream.read(fileBytes);
                    } catch (Exception e) {
                        rglog.error(e.getMessage());
                    } finally {
                        //关闭流
                        try {
                            if (null != fileInputStream) {
                                fileInputStream.close();
                            }
                        } catch (Exception e) {
                            rglog.error(e.getMessage());
                        }
                    }

                    //预防空指针
                    if (null != fileBytes) {
                        radpString = new String(fileBytes);
                    }
                    rglog.debug("获取参数:" + radpString);
                    deleteFile(jsonFile.getAbsolutePath());
                }
            }

            JSONObject jsonObject = JSONObject.parseObject(radpString);
            jsonObject.put("imageData", imgBase64String);
            jsonObject.put("imageName", imageName);
            // xmlTreeUtil.setXmlTreeStringValue("imageData", imgBase64String);
            String signBlockString = generateSignBlockString(radpString);
            xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.SIGN_BLOCK, signBlockString);
            radpString = jsonObject.toString();
            return radpString.getBytes();
        }

        //获取验签串
        String signBlockString = generateSignBlockString(jsonStr);
        xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.SIGN_BLOCK, signBlockString);
        return jsonStr.getBytes();
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID);

        /* 获取交易配置信息 */
        TxnInfoService txnInfoService = new TxnInfoServiceImpl();
        TxnInfo txnInfo = txnInfoService.getCurrentTxnInfo();
        xmlTreeUtil.setXmlTreeObjectValue(TreeNodeConstant.TXN_INFO,txnInfo);

        /* 判断是否要验签 */
        xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.YES);
        if (AppParamConstant.YES.equals(txnInfo.getSrcTxnAuthCfg().getSignFlag())) {
            String signBlockString = xmlTreeUtil.getXmlTreeStringValue(BompTreeNodeConstant.SIGN_BLOCK);
            String signature = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_SIGNATURE);
            /*查询redis获取签名密钥*/
            String sessionId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_SESSION);
            if (sessionId == null || "".equals(sessionId.trim())) {
                xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.NO);
                return 0;
            }
            String sessionObjStr = RedisUtil.onceGet(sessionId);
            if (sessionObjStr == null) {
                rglog.error("根据会话标识<{}>获取会话缓存失败", sessionId);
                xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.NO);
            }

            UserSessionInfo userSessionInfo = JSON.parseObject(sessionObjStr, UserSessionInfo.class);
            if (null == userSessionInfo) {
                xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.NO);
                return 0;
            }
            xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.SIGN_KEY, userSessionInfo.getSignatureKey());
            SysParamService sysParamService = new SysParamServiceImpl();
            SysParam signBlockParam = sysParamService.getSysParamInfo(instId, BompAppParamConstant.MCHNT_PARAM_TYPE, BompAppParamConstant.SIGN_BLOCK_PARAM);
            if (null != signBlockParam && "N".equals(signBlockParam.getParamValue())) {
                rglog.info("不验签");
                xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.YES);
            } else {
                rglog.info("验签交易");
                rglog.info("验签key：" + userSessionInfo.getSignatureKey());
                if (!MD5Util.verifySignString(signature, signBlockString, "&key=", userSessionInfo.getSignatureKey())) {
                    rglog.error("验签失败!sign:<{}>，signBlock:<{}>", signature, signBlockString);
                    xmlTreeUtil.setXmlTreeStringValue(BompTreeNodeConstant.VERIFY_SIGN_FLAG, AppParamConstant.NO);
                }
            }
        }

        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();

        xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/ArgList", "Content-Type");
        String txnNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TXN_NUM);
        TxnInfo txnInfo = (TxnInfo) xmlTreeUtil.getXmlTreeObjectValue(TreeNodeConstant.TXN_INFO, TxnInfo.class);
        if (null == txnInfo) {
            TxnInfoService txnInfoService = new TxnInfoServiceImpl();
            txnInfo = txnInfoService.getCurrentTxnInfo();
        }

        /*图片上传下载处理*/
        String respCode = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.RESP_CODE);
        if ("T00BM001".equals(txnNum) && RespCodeEnum.TRANS_SUCCESS.getRespCode().equals(respCode)) {
            String isGetRequest = xmlTreeUtil.getXmlTreeStringValue(BompTreeNodeConstant.IS_GET_REQUEST);
            String isFormRequest = xmlTreeUtil.getXmlTreeStringValue(BompTreeNodeConstant.IS_FORM_REQUEST);
            if ("Y".equals(isGetRequest)) {
                String realImagePath = xmlTreeUtil.getXmlTreeStringValue(BompTreeNodeConstant.REAL_IMAGE_PATH);
                File imageFile = new File(realImagePath);
                if (!imageFile.exists()) {
                    rglog.error("文件<{}>不存在!", realImagePath);
                    throw new BizException(RespCodeEnum.FILE_NOT_EXIST.getRespCode(), RespCodeEnum.FILE_NOT_EXIST.getRespDesc());
                }
                String contentType = Files.probeContentType(Paths.get(realImagePath));
                xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Content-Type", contentType + ";charset=utf-8");
                // xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Content-length", String.valueOf(imageFile.length()));
                // xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Content-disposition", "attachment;filename=" + imageFile.getName());

                //IO流操作
                FileInputStream fileInputStream = null;
                byte[] downloadFileBytes = null;
                try {
                    fileInputStream = new FileInputStream(imageFile);
                    downloadFileBytes = new byte[fileInputStream.available()];
                    fileInputStream.read(downloadFileBytes);
                } catch (Exception e) {
                    rglog.error(e.getMessage());
                } finally {
                    //关闭流
                    try {
                        if (null != fileInputStream) {
                            fileInputStream.close();
                        }
                    } catch (Exception e) {
                        rglog.error(e.getMessage());
                    }
                }

                return downloadFileBytes;
            } else if ("Y".equals(isFormRequest)) {
                //图片上传的表单提交特殊处理
                xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Content-Type", "text/plain;charset=utf-8");
            } else {
                xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Content-Type", "application/json;charset=utf-8");
            }
        } else {
            xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Content-Type", "application/json;charset=utf-8");
        }
        if (txnInfo.getSrcTxnAuthCfg().getTxnNum().equals("HEALCHTK") || txnInfo.getSrcTxnAuthCfg().getTxnNum().equals("M00BM063")) {
            return new byte[0];
        }
        if (AppParamConstant.NO.equals(txnInfo.getSrcTxnAuthCfg().getSignFlag())) {
            return bytes;
        }

        String jsonBefore = new String(bytes);
        String signBlock = generateSignBlockString(jsonBefore);
        /* 生成生成数字签名的字符串 */
        if (null == signBlock) {
            rglog.error("生成加签Block失败!");
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
//        rglog.debug("加签Block:<{}>", signBlock);

        String md5Key = xmlTreeUtil.getXmlTreeStringValue(BompTreeNodeConstant.SIGN_KEY);
        String signData = MD5Util.generateSignString(signBlock, "&key=", md5Key);
        if (null == signData) {
            rglog.error("生成数字签名失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        rglog.debug("数字签名:<{}>", signData);

        JSONObject jsonObj = JSON.parseObject(jsonBefore, Feature.OrderedField);
        JSONObject headObj = jsonObj.getJSONObject(BompTreeNodeConstant.HEADER);
        headObj.put(BompTreeNodeConstant.SIGNATURE, signData);
        jsonObj.put(BompTreeNodeConstant.HEADER, headObj);
//        rglog.debug("添加signature后发送报文:<{}>", jsonObj.toJSONString());
        return jsonObj.toJSONString().getBytes();
    }

    /**
     * 删除文件
     *
     * @param fileName 绝对路径下的文件名
     * @return 删除结果
     */
    private boolean deleteFile(String fileName) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        File file = new File(fileName);
        if (file.isFile() && file.exists()) {
            if (!file.delete()) {
                rglog.error("删除文件<{}>失败", fileName);
                return false;
            } else {
                rglog.debug("删除文件<{}>成功", fileName);
                return true;
            }
        } else {
            return false;
        }
    }

    /**
     * 生成加签Block
     *
     * @param jsonString 原始报文
     * @return 加签BLOCK
     */
    private String generateSignBlockString(String jsonString) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        try {
            if (null != jsonString && 0 != jsonString.trim().length()) {
//                rglog.debug("jsonString=[{}]", jsonString);
                JSONObject jsonObject = JSONObject.parseObject(jsonString, Feature.OrderedField);
                ArrayList<String> arrayList = new ArrayList<>();
                Map maps = jsonObject.getInnerMap();
                int i = 0;
                Object key;
                Object value;
                for (Object map : maps.entrySet()) {
                    key = ((Map.Entry) map).getKey();
                    value = ((Map.Entry) map).getValue();
                    if (!BompTreeNodeConstant.HEADER.equals(key) && null != value) {
                        arrayList.add(key + "=" + value + "&");
                    } else if (BompTreeNodeConstant.HEADER.equals(key) && null != value) {
                        JSONObject headObj = JSONObject.parseObject(value.toString(), Feature.OrderedField);
                        headObj.remove(BompTreeNodeConstant.SIGNATURE);
                        arrayList.add(key + "=" + headObj + "&");
                    }
                    i++;
                }
                String[] strArray = arrayList.toArray(new String[arrayList.size()]);
                Arrays.sort(strArray);
                StringBuffer stringBuffer = new StringBuffer();
                for (i = 0; i < strArray.length; i++) {
                    stringBuffer.append(strArray[i]);
                }
                return stringBuffer.substring(0, stringBuffer.length() - 1).toString();
            }
        } catch (Exception e) {
            rglog.error("系统异常!");
            return null;
        }
        return null;
    }

}
